package com.yalantis.guillotine.animation;

import com.yalantis.guillotine.animation.anim.AnimatorTypes;
import com.yalantis.guillotine.animation.anim.XAnimatorValue;
import com.yalantis.guillotine.interfaces.GuillotineListener;
import com.yalantis.guillotine.interfaces.TimeInterpolator;
import com.yalantis.guillotine.util.GuillotineInterpolator;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.components.Component;


public class GuillotineAnimation {
    private AnimatorGroup animatoropen;
    private AnimatorGroup animatorclose;
    private static final String ROTATION = "rotation";
    private static final float GUILLOTINE_CLOSED_ANGLE = -90f;
    private static final float GUILLOTINE_OPENED_ANGLE = 0f;
    private static final int DEFAULT_DURATION = 625;
    private static final float ACTION_BAR_ROTATION_ANGLE = 3f;

    private final Component mGuillotineView;
    private final long mDuration;
    private final XAnimatorValue mOpeningAnimation;
    private final XAnimatorValue mClosingAnimation;
    private final GuillotineListener mListener;
    private final boolean mIsRightToLeftLayout;
    private final TimeInterpolator mInterpolator;
    private final Component mActionBarView;
    private final long mDelay;
    private boolean isOpening;
    private boolean isClosing;

    private GuillotineAnimation(GuillotineBuilder builder) {
        animatoropen = new AnimatorGroup();
        animatorclose = new AnimatorGroup();
        this.mActionBarView = builder.actionBarView;
        this.mListener = builder.guillotineListener;
        this.mGuillotineView = builder.guillotineView;
        this.mDuration = builder.duration > 0 ? builder.duration : DEFAULT_DURATION;
        this.mDelay = builder.startDelay;
        this.mIsRightToLeftLayout = builder.isRightToLeftLayout;
        this.mInterpolator = builder.interpolator == null ? new GuillotineInterpolator() : builder.interpolator;
        setUpOpeningView(builder.openingView);
        setUpClosingView(builder.closingView);
        this.mOpeningAnimation = buildOpeningAnimation();
        this.mClosingAnimation = buildClosingAnimation();
        if (builder.isClosedOnStart) {
            mGuillotineView.setRotation(GUILLOTINE_CLOSED_ANGLE);
            mGuillotineView.setVisibility(Component.INVISIBLE);
        }
    }

    public void open() {
        if (!isOpening) {
            animatoropen.start();
        }
    }

    public void close() {
        if (!isClosing) {
            animatorclose.start();
        }
    }

    private void setUpOpeningView(final Component openingView) {
        if (mActionBarView != null) {
            mActionBarView.setLayoutRefreshedListener(new Component.LayoutRefreshedListener() {
                @Override
                public void onRefreshed(Component component) {
                    mActionBarView.setLayoutRefreshedListener(null);
                    mActionBarView.setPivotX(calculatePivotX(openingView));
                    mActionBarView.setPivotY(calculatePivotY(openingView));
                }
            });
        }

        openingView.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component v) {
                open();
            }
        });
    }

    private void setUpClosingView(final Component closingView) {
        mGuillotineView.setLayoutRefreshedListener(new Component.LayoutRefreshedListener() {
            @Override
            public void onRefreshed(Component component) {
                mGuillotineView.setLayoutRefreshedListener(null);
                mGuillotineView.setPivotX(calculatePivotX(closingView));
                mGuillotineView.setPivotY(calculatePivotY(closingView));
            }
        });

        closingView.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component v) {
                close();
            }
        });
    }

    private XAnimatorValue buildOpeningAnimation() {
        XAnimatorValue rotationAnimator = initAnimator(XAnimatorValue.ofFloat(mGuillotineView, AnimatorTypes.ROTATE, mDuration, GUILLOTINE_CLOSED_ANGLE, GUILLOTINE_OPENED_ANGLE));
        animatoropen.runSerially(rotationAnimator);
        animatoropen.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animation) {
                mGuillotineView.setVisibility(Component.VISIBLE);
                rotationAnimator.start();
                isOpening = true;
            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onEnd(Animator animation) {
                isOpening = false;
                if (mListener != null) {
                    mListener.onGuillotineOpened();
                }
                rotationAnimator.end();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }

            @Override
            public void onCancel(Animator animation) {
            }

        });
        return rotationAnimator;
    }


    private XAnimatorValue buildClosingAnimation() {
        XAnimatorValue rotationAnimator = initAnimator(XAnimatorValue.ofFloat(mGuillotineView, AnimatorTypes.ROTATE, (long) (mDuration * GuillotineInterpolator.ROTATION_TIME), GUILLOTINE_OPENED_ANGLE, GUILLOTINE_CLOSED_ANGLE));
        animatorclose.runSerially(rotationAnimator);
        animatorclose.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                isClosing = true;
                mGuillotineView.setVisibility(Component.VISIBLE);
                rotationAnimator.start();
            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                isClosing = false;
                mGuillotineView.setVisibility(Component.HIDE);

                if (mListener != null) {
                    mListener.onGuillotineClosed();
                }
                rotationAnimator.end();
                startActionBarAnimation();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        return rotationAnimator;
    }

    private void startActionBarAnimation() {
        AnimatorGroup actionOpen = new AnimatorGroup();
        XAnimatorValue actionBarAnimation = XAnimatorValue.ofFloat(mActionBarView, AnimatorTypes.ROTATE, (long) (mDuration * (GuillotineInterpolator.FIRST_BOUNCE_TIME + GuillotineInterpolator.SECOND_BOUNCE_TIME)), GUILLOTINE_OPENED_ANGLE, ACTION_BAR_ROTATION_ANGLE, GUILLOTINE_OPENED_ANGLE);
        actionOpen.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                actionBarAnimation.start();
            }

            @Override
            public void onStop(Animator animator) {
                actionBarAnimation.end();
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        actionOpen.runSerially(actionBarAnimation);
        actionOpen.start();
    }

    private XAnimatorValue initAnimator(XAnimatorValue animator) {
        animator.setDelay(mDelay);
        return animator;
    }

    private float calculatePivotY(Component burger) {
        return burger.getTop() + burger.getHeight() / 2;
    }

    private float calculatePivotX(Component burger) {
        return burger.getLeft() + burger.getWidth() / 2;
    }

    public static class GuillotineBuilder {
        private final Component guillotineView;
        private final Component openingView;
        private final Component closingView;
        private Component actionBarView;
        private GuillotineListener guillotineListener;
        private long duration;
        private long startDelay;
        private boolean isRightToLeftLayout;
        private TimeInterpolator interpolator;
        private boolean isClosedOnStart;

        public GuillotineBuilder(Component guillotineView, Component closingView, Component openingView) {
            this.guillotineView = guillotineView;
            this.openingView = openingView;
            this.closingView = closingView;
        }

        public GuillotineBuilder setActionBarViewForAnimation(Component view) {
            this.actionBarView = view;
            return this;
        }

        public GuillotineBuilder setGuillotineListener(GuillotineListener guillotineListener) {
            this.guillotineListener = guillotineListener;
            return this;
        }

        public GuillotineBuilder setDuration(long duration) {
            this.duration = duration;
            return this;
        }

        public GuillotineBuilder setStartDelay(long startDelay) {
            this.startDelay = startDelay;
            return this;
        }

        public GuillotineBuilder setRightToLeftLayout(boolean isRightToLeftLayout) {
            this.isRightToLeftLayout = isRightToLeftLayout;
            return this;
        }

        public GuillotineBuilder setInterpolator(TimeInterpolator interpolator) {
            this.interpolator = interpolator;
            return this;
        }

        public GuillotineBuilder setClosedOnStart(boolean isClosedOnStart) {
            this.isClosedOnStart = isClosedOnStart;
            return this;
        }

        public GuillotineAnimation build() {
            return new GuillotineAnimation(this);
        }
    }
}
